home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / ISSUE23 / SURVIVE / multgrid.pas < prev    next >
Pascal/Delphi Source File  |  1997-05-19  |  12KB  |  420 lines

  1. unit MultGrid;
  2.  
  3. { Note:  When using the TDBMultiGrid component, if you repopulate the dataset while
  4.   within a DisableControls/EnableControls block the selected rows will not be cleared.
  5.   For example:
  6.  
  7.     with Query1 do
  8.     begin
  9.       DisableControls;
  10.       try
  11.         Close;
  12.         Open; (*Presumably with new parameter settings *)
  13.       finally
  14.         EnableControls;
  15.       end;
  16.     end;
  17.  
  18.   Any rows that were selected prior to this routine will remain selected.  However,
  19.   if you close the dataset outside of the DisableControls/EnableControls block, the
  20.   selected rows are cleared:
  21.  
  22.     with Query1 do
  23.     begin
  24.       Close;
  25.       DisableControls;
  26.       try
  27.         Open; (*Presumably with new parameter settings *)
  28.       finally
  29.         EnableControls;
  30.       end;
  31.     end;
  32.  
  33. }
  34.  
  35. interface
  36.  
  37. uses
  38.   WinTypes, Classes, Controls, DB, DBGrids, Grids;
  39.  
  40. type
  41.   TMultiGridSelectingEvent = procedure (Sender: TObject; var Selected: Boolean) of object;
  42.  
  43.   TDBMultiGrid = class(TDBGrid)
  44.   private
  45.     FSelectedList: TList;
  46.   protected
  47.     FAllowRedraw: Boolean;
  48.     FAllowRedrawLevel: Integer;
  49.     FAutoSelect: Boolean;
  50.     FDataField: string;
  51.     FDefaultDrawing: Boolean; { This mimicks the inherited DefaultDrawing property; see DrawCell }
  52.     FOldStateChangeHandler: TNotifyEvent;
  53.     FOldKeyDownHandler: TKeyEvent;
  54.     FOldDblClickHandler: TNotifyEvent;
  55.  
  56.     FOnSelecting: TMultiGridSelectingEvent;
  57.     FOnSelected: TNotifyEvent;
  58.     procedure DoDblClick(Sender: TObject);
  59.     procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  60.     procedure DoStateChange(Sender: TObject);
  61.     function GetAnySelected: Boolean;
  62.     function GetSelected: Boolean;
  63.     procedure SetSelected(Value: Boolean);
  64.     function GetDefaultDrawing: Boolean;
  65.     procedure SetAllowRedraw(Value: Boolean);
  66.     procedure SetDefaultDrawing(Value: Boolean);
  67.     procedure Loaded; override;
  68.     procedure SetAutoSelect(Value: Boolean);
  69.   public
  70.     constructor Create(AOwner: TComponent); override;
  71.     destructor Destroy;
  72.     procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
  73.     procedure DrawDataCell(const Rect: TRect; Field: TField; State: TGridDrawState); override;
  74.     procedure GetSelectedValues(List: TList);
  75.     procedure SelectAll(Switch: Boolean);
  76.     procedure SetSelectedValues(List: TList);
  77.  
  78.     property AllowRedraw: Boolean read FAllowRedraw write SetAllowRedraw;
  79.     property AnySelected: Boolean read GetAnySelected;
  80.     property Selected: Boolean read GetSelected write SetSelected;
  81.   published
  82.     property AutoSelect: Boolean read FAutoSelect write SetAutoSelect default True;
  83.     property DataField: string read FDataField write FDataField;
  84.     property DefaultDrawing: Boolean read GetDefaultDrawing write SetDefaultDrawing default True;
  85.     property OnClick;
  86.     property OnSelected: TNotifyEvent read FOnSelected write FOnSelected;
  87.     property OnSelecting: TMultiGridSelectingEvent read FOnSelecting write FOnSelecting;
  88.   end;
  89.  
  90.   procedure Register;
  91.  
  92. implementation
  93.  
  94. uses
  95.   WinProcs, Graphics, SysUtils, DbTables;
  96.  
  97. { TDBMultiGrid }
  98.  
  99. constructor TDBMultiGrid.Create(AOwner: TComponent);
  100. begin
  101.   inherited Create(AOwner);
  102.   FSelectedList := TList.Create;
  103.   FDataField := '';
  104.   FDefaultDrawing := True;
  105.   FAllowRedraw := True;
  106.   FAllowRedrawLevel := 0;
  107.   FAutoSelect := True;  { default }
  108.   Options := Options + [dgRowSelect];
  109. end;
  110.  
  111. destructor TDBMultiGrid.Destroy;
  112. begin
  113.   FSelectedList.Free;
  114.   inherited Destroy;
  115. end;
  116.  
  117. procedure TDBMultiGrid.DoStateChange(Sender: TObject);
  118. begin
  119.   if DataSource <> nil then
  120.     if DataSource.State = dsInactive then
  121.       FSelectedList.Clear;
  122.  
  123.   if Assigned(FOldStateChangeHandler) then
  124.     FOldStateChangeHandler(Sender);
  125. end;
  126.  
  127. procedure TDBMultiGrid.DoDblClick(Sender: TObject);
  128. begin
  129.   if AutoSelect then
  130.     Selected := not Selected;
  131.  
  132.   if Assigned(FOldDblClickHandler) then
  133.     FOldDblClickHandler(Sender);
  134. end;
  135.  
  136. procedure TDBMultiGrid.DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  137. begin
  138.   if AutoSelect then
  139.   begin
  140.     if Key = VK_SPACE then
  141.       Selected := not Selected;
  142.   end;
  143.  
  144.   if Assigned(FOldKeyDownHandler) then
  145.     FOldKeyDownHandler(Sender, Key, Shift);
  146. end;
  147.  
  148. procedure TDBMultiGrid.Loaded;
  149. begin
  150.   inherited Loaded;
  151.   FOldKeyDownHandler := OnKeyDown;
  152.   OnKeyDown := DoKeyDown;
  153.   FOldDblClickHandler := OnDblClick;
  154.   OnDblClick := DoDblClick;
  155.  
  156.   if Datasource <> nil then
  157.   begin
  158.     FOldStateChangeHandler := DataSource.OnStateChange;
  159.     DataSource.OnStateChange := DoStateChange;
  160.   end;
  161. end;
  162.  
  163. function TDBMultiGrid.GetAnySelected: Boolean;
  164. begin
  165.   Result := FSelectedList.Count <> 0;
  166. end;
  167.  
  168. function TDBMultiGrid.GetSelected: Boolean;
  169. begin
  170.   Result := False;
  171.   if DataSource <> nil then
  172.     Result := FSelectedList.IndexOf(Pointer(DataSource.DataSet.FieldByName(FDataField).AsInteger)) <> -1;
  173. end;
  174.  
  175. procedure TDBMultiGrid.SetSelected(Value: Boolean);
  176. var
  177.   Index: LongInt;
  178. begin
  179.   if DataSource <> nil then
  180.   begin
  181.     if DataSource.DataSet.FieldByName(FDataField).IsNull then Exit;
  182.     Index := DataSource.DataSet.FieldByName(FDataField).AsInteger;
  183.  
  184.     if Value <> (FSelectedList.IndexOf(Pointer(Index)) <> -1) then
  185.     begin
  186.       if Assigned(FOnSelecting) then FOnSelecting(Self, Value);
  187.       if Value <> (FSelectedList.IndexOf(Pointer(Index)) <> -1) then
  188.       begin
  189.         if Value then
  190.           FSelectedList.Add(Pointer(Index))
  191.         else
  192.           FSelectedList.Delete(FSelectedList.IndexOf(Pointer(Index)));
  193.  
  194.         { Value could have been changed by FOnSelecting }
  195.         if FAllowRedraw then Repaint;
  196.         if Assigned(FOnSelected) then FOnSelected(Self);
  197.       end;
  198.     end;
  199.   end;
  200. end;
  201.  
  202. procedure TDBMultiGrid.SetAllowRedraw(Value: Boolean);
  203. begin
  204.   if Value then
  205.   begin
  206.     Dec(FAllowRedrawLevel);
  207.     if FAllowRedrawLevel <= 0 then
  208.     begin
  209.       FAllowRedrawLevel := 0;
  210.       Repaint;
  211.       FAllowRedraw := True;
  212.     end;
  213.   end
  214.   else
  215.   begin
  216.     Inc(FAllowRedrawLevel);
  217.     FAllowRedraw := False;
  218.   end;
  219. end;
  220.  
  221. procedure TDBMultiGrid.SetAutoSelect(Value: Boolean);
  222. begin
  223.   if Value <> FAutoSelect then
  224.     FAutoSelect := Value;
  225. end;
  226.  
  227. function TDBMultiGrid.GetDefaultDrawing: Boolean;
  228. begin
  229.   Result := inherited DefaultDrawing;
  230. end;
  231.  
  232. procedure TDBMultiGrid.SetDefaultDrawing(Value: Boolean);
  233. begin
  234.   FDefaultDrawing := Value;
  235.   inherited DefaultDrawing := Value;
  236. end;
  237.  
  238. procedure TDBMultiGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
  239. { This is a kludge.  The default drawing behavior of the TDBGrid is being changed by this
  240.   descendant.  However, the routine where this is done, DefaultDrawDataCell, is not
  241.   virtual, so we must override the DrawCell method (which is responsible for calling
  242.   DefaultDrawDataCell).  We need to prevent DrawCell from calling DefaultDrawDataCell
  243.   by forcing the inherited DefaultDrawing property to false.  But we must preserve the
  244.   state of the DefaultDrawing property to allow user-defined cell drawing event handlers
  245.   for this descendant. }
  246. begin
  247.   { Force DefaultDrawing to false to prevent the inherited TDBGrid's DefaultDrawDataCell
  248.     from executing.  Our own default drawing will take place in the DrawDataCell method. }
  249.   inherited DefaultDrawing := False;
  250.   inherited DrawCell(ACol, ARow, ARect, AState);
  251.  
  252.   { Restore the state of the DefaultDrawing property }
  253.   inherited DefaultDrawing := FDefaultDrawing;
  254. end;
  255.  
  256. procedure TDBMultiGrid.DrawDataCell(const Rect: TRect; Field: TField;
  257.   State: TGridDrawState);
  258. var
  259.   DrawFormat: Integer;
  260.   FieldText: array[0..255] of Char;
  261.   ARect: TRect;
  262.   BackgroundColor: TColor;
  263.   ForegroundColor: TColor;
  264. begin
  265.   if FDefaultDrawing then  { Do NOT query the inherited DefaultDrawing property }
  266.   begin
  267.     DrawFormat := DT_LEFT;
  268.     FillChar(FieldText, SizeOf(FieldText), 0);
  269.  
  270.     { Capture field information; DisplayText provides cell formatting }
  271.     if Field <> nil then
  272.     begin
  273.       StrPCopy(FieldText, Field.DisplayText);
  274.       case Field.Alignment of
  275.         taRightJustify: DrawFormat := DT_RIGHT;
  276.         taCenter: DrawFormat := DT_CENTER;
  277.       end;
  278.     end;
  279.  
  280.     { Set highlight colors if row is selected }
  281.     if (FDataField <> '') and Selected then
  282.     begin
  283.       BackgroundColor := clHighlight;
  284.       ForegroundColor := clHighlightText;
  285.     end
  286.     else
  287.     begin
  288.       BackgroundColor := Color;
  289.       ForegroundColor := Font.Color;
  290. (*      if gdSelected in State then
  291.       begin
  292.         BackgroundColor := clWindow;
  293.         ForegroundColor := clWindowText;
  294.       end
  295.       else
  296.       begin
  297.         BackgroundColor := Canvas.Brush.Color;
  298.         ForegroundColor := Canvas.Font.Color;
  299.       end;*)
  300.     end;
  301.  
  302.     { Adjust the rectangle to draw in the same boundaries that TDBGrid draws }
  303.     Move(Rect, ARect, SizeOf(Rect));
  304.     Inc(ARect.Top, 2);
  305.     case DrawFormat of
  306.       DT_LEFT: Inc(ARect.Left, 2);
  307.       DT_RIGHT: Dec(ARect.Right, 3);
  308.       DT_CENTER: begin
  309.         Inc(ARect.Left);
  310.         Dec(ARect.Right);
  311.       end;
  312.     end;
  313.  
  314.     { Set and paint the cell background color }
  315.     Canvas.Brush.Color := BackgroundColor;
  316.     Canvas.FillRect(Rect);  { use the original rectangle }
  317.  
  318.     { Draw the text in the cell }
  319.     Canvas.Font.Color := ForegroundColor;
  320.     DrawText(Canvas.Handle, FieldText, -1, ARect, DrawFormat);
  321.  
  322.     { Draw a focused cell if needed }
  323.     if (gdFocused in State) and not (dgRowSelect in Options) then
  324.       Canvas.DrawFocusRect(Rect);
  325.   end;
  326.  
  327.   inherited DrawDataCell(Rect, Field, State);
  328. end;
  329.  
  330. procedure TDBMultiGrid.GetSelectedValues(List: TList);
  331. { Returns a list of the index values for all selected rows.  The TList returns does
  332.   not contain pointers to objects, but the value of the pointers are in fact the
  333.   index values of the selected rows (if typecast to LongInt). }
  334.  
  335. var
  336.   BMark: TBookmark;
  337. begin
  338.  
  339.   { Loop through the records.  Originally, the ForAll method of the Orpheus sparse
  340.     array was used, but this produced a list of table in Drop_ID order, which is
  341.     not necessarily the same as the display order. }
  342.  
  343.   AllowRedraw := False;
  344.   with DataSource.DataSet do
  345.   begin
  346.     DisableControls;
  347.     try
  348.       BMark := GetBookmark;
  349.       First;
  350.       while not Eof do
  351.       begin
  352.         if Selected then
  353.           List.Add(Pointer(FieldByName(FDataField).AsInteger));
  354.         Next;
  355.       end;
  356.       GotoBookmark(BMark);
  357.     finally
  358.       EnableControls;
  359.       FreeBookmark(BMark);
  360.     end;
  361.   end;
  362.   AllowRedraw := True;
  363. end;
  364.  
  365. procedure TDBMultiGrid.SetSelectedValues(List: TList);
  366. { Given a list of index values (LongInts in place of the object pointers), marks
  367.   those rows as selected (any existing selected rows remain selected).}
  368. var
  369.   I: Integer;
  370.   Value: Boolean;
  371. begin
  372.   AllowRedraw := False;
  373.   for I := 0 to List.Count - 1 do
  374.   begin
  375.     Value := True;
  376.     if Assigned(FOnSelecting) then FOnSelecting(Self, Value);
  377.     if Value then FSelectedList.Add(List[I])
  378.     else if FSelectedList.IndexOf(List[I]) <> -1 then
  379.       FSelectedList.Delete(FSelectedList.IndexOf(List[I]));
  380.     if Assigned(FOnSelected) then FOnSelected(Self);
  381.   end;
  382.   AllowRedraw := True;
  383. end;
  384.  
  385. procedure TDBMultiGrid.SelectAll(Switch: Boolean);
  386. var
  387.   BMark: TBookmark;
  388. begin
  389.  
  390.   { Loop through the records so the OnSelecting and OnSelected events fire }
  391.  
  392.   AllowRedraw := False;
  393.   with DataSource.DataSet do
  394.   begin
  395.     DisableControls;
  396.     try
  397.       BMark := GetBookmark;
  398.       First;
  399.       while not Eof do
  400.       begin
  401.         Selected := Switch;
  402.         Next;
  403.       end;
  404.       GotoBookmark(BMark);
  405.     finally
  406.       EnableControls;
  407.       FreeBookmark(BMark);
  408.     end;
  409.   end;
  410.   AllowRedraw := True;
  411. end;
  412.  
  413. procedure Register;
  414. begin
  415.   RegisterComponents('Oasis', [TDBMultiGrid]);
  416. end;
  417.  
  418.  
  419. end.
  420.